package com.zkjl.protect_business.service.impl;



import cn.hutool.core.collection.CollUtil;
import cn.hutool.crypto.digest.DigestAlgorithm;
import cn.hutool.crypto.digest.Digester;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.google.common.collect.Lists;
import com.zkjl.protect_business.common.BaseException;
import com.zkjl.protect_business.common.UserThreadLocal;
import com.zkjl.protect_business.dto.OrganizationDTO;
import com.zkjl.protect_business.entity.OrgField;
import com.zkjl.protect_business.entity.Organization;
import com.zkjl.protect_business.repository.OrganizationRepository;
import com.zkjl.protect_business.service.LogService;
import com.zkjl.protect_business.service.OrganizationService;
import com.zkjl.protect_business.service.UserService;
import com.zkjl.protect_business.vo.OrganizationVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.zkjl.protect_business.common.Consts.*;
import static com.zkjl.protect_business.common.Status.DATA_EXIST;
import static com.zkjl.protect_business.common.Status.DATA_NOT_EXIST;

@Service
@Slf4j
public class OrganizationServiceImpl implements OrganizationService {

    @Autowired
    ModelMapper modelMapper;
    @Autowired
    OrganizationRepository organizationRepository;
    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Autowired
    LogService logService;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    UserService userService;


    @Override
    public Organization saveOrUpdate(OrganizationDTO organizationDTO, String id) {

        Organization organization = modelMapper.map(organizationDTO, Organization.class);

        //验证编号和名称都不能重复
        Boolean isverifyName = false;   //是否需要验证名字重复
        String orgName1 = organization.getOrgName();
        if (StringUtils.isNotBlank(orgName1) && StringUtils.isBlank(id) ) {
            isverifyName = true;
        }

        if (StringUtils.isNotBlank(id)) {
            Organization organization1 = get(id);
            String orgName = organization1.getOrgName();
            if (!orgName.equals(orgName1)) {
                isverifyName = true;
            }
        }

        if (isverifyName) {
            List<Organization> allByOrgName = organizationRepository.findAllByOrgName(orgName1);
            if (CollUtil.isNotEmpty(allByOrgName)) {
                throw new BaseException(DATA_EXIST.getCode(), "机构名称已存在，请确认后重试！");
            }
        }

        //验证编号
        Boolean isverifyNum = false;
        String orgNum = organization.getOrgNum();
        if (StringUtils.isNotBlank(orgNum) && StringUtils.isBlank(id) ) {
            isverifyNum = true;
        }

        if (StringUtils.isNotBlank(id)) {
            Organization organization1 = get(id);
            String orgNum1 = organization1.getOrgNum();
            if (StringUtils.isNotBlank(orgNum1) && !orgNum1.equals(orgNum)) {
                isverifyNum = true;
            }
        }

        if (isverifyNum) {
            List<Organization> allByOrgName = organizationRepository.findAllByOrOrgNum(orgNum);
            if (CollUtil.isNotEmpty(allByOrgName)) {
                throw new BaseException(DATA_EXIST.getCode(), "机构编号已存在，请确认后重试！");
            }
        }


        String pid = organization.getPid();

        //没有设置pid的默认path就是 /
        organization.setPath("/");
        if (StringUtils.isNotEmpty(pid)) {
            Optional<Organization> repository = organizationRepository.findById(pid);
            if (!repository.isPresent()) {
                throw new BaseException(DATA_NOT_EXIST.getCode(), "未找到该父级单位信息！");
            }
            Organization pOrganization = repository.get();
            String orgName = pOrganization.getOrgName();
            String path = pOrganization.getPath();
            organization.setPname(orgName);
            organization.setPath(path + pid + "/");
        }

        if (StringUtils.isBlank(id)) {
            Digester md5 = new Digester(DigestAlgorithm.MD5);
            id = md5.digestHex(JSONUtil.toJsonStr(organization));
            organization.setBaseInfo();
            logService.save("单位", organization.getOrgName(), OPERATION_ADD);
        }else {
            logService.save("单位", organization.getOrgName(), OPERATION_EDIT);
        }

        organization.setId(id);

        Organization save = organizationRepository.save(organization);

        return save;
    }

    @Override
    public Organization update(OrganizationDTO organizationDTO, String id) {

        Organization organization = get(id);
        String orgNum = organizationDTO.getOrgNum();
        if (StringUtils.isNotBlank(orgNum)) {
            List<Organization> allByOrgName = organizationRepository.findAllByOrOrgNumAndIdNot(orgNum, id);
            if (CollUtil.isNotEmpty(allByOrgName)) {
                throw new BaseException(DATA_EXIST.getCode(), "机构编号已存在，请确认后重试！");
            }
            organization.setOrgNum(orgNum);
        }

        Integer number = organizationDTO.getNumber();
        if (number != null) {
            organization.setNumber(number);
        }

        String region = organizationDTO.getRegion();
        if (StringUtils.isNotBlank(region)) {
            organization.setRegion(region);
        }

        List<OrgField> fields = organizationDTO.getFields();
        if (CollUtil.isNotEmpty(fields)) {
            organization.setFields(fields);
        }
        String orgName = organizationDTO.getOrgName();
        if (StringUtils.isNotBlank(orgName)) {
            List<Organization> allByOrgName = organizationRepository.findAllByOrgNameAndIdNot(orgName, id);
            if (CollUtil.isNotEmpty(allByOrgName)) {
                throw new BaseException(DATA_EXIST.getCode(), "机构名称已存在，请确认后重试！");
            }
            organization.setOrgName(orgName);
        }

        Organization save = organizationRepository.save(organization);

        //修改单位之后更新该单位下的所有用户信息
        userService.updateOrgInfo(save.getId(), save.getOrgName());

        //修改该单位下级的父级名称
        List<Organization> allByPid = organizationRepository.findAllByPid(id);
        if (CollUtil.isNotEmpty(allByPid)) {
            allByPid.stream().forEach(organization1 -> {
                organization1.setPname(save.getOrgName());
                organizationRepository.save(organization1);
            });
        }


        logService.save("单位", organization.getOrgName(), OPERATION_EDIT);

        return save;
    }

    @Override
    public List<OrganizationVO> getOrganizationTree() {

        String orgId = UserThreadLocal.getOrgId();
        //本部数据
        Organization organization = get(orgId);
        OrganizationVO organizationVO = modelMapper.map(organization, OrganizationVO.class);
        organizationVO.setChildren(getChildren(orgId));

        List<OrganizationVO> organizationVOS = Lists.newArrayList(organizationVO);

        return organizationVOS;
    }

    @Override
    public List<Organization> list() {

        Iterable<Organization> all = organizationRepository.findAll();
        ArrayList<Organization> organizations = Lists.newArrayList(all);

        return organizations;
    }

    @Override
    public List<String> listChildrenIds(String pid) {

        List<String> result = new ArrayList<>();

        List<Organization> allByPid = organizationRepository.findAllByPid(pid);
        List<String> collect = allByPid.stream().map(organization -> {
            String id = organization.getId();
            List<String> list = listChildrenIds(id);
            result.addAll(list);
            return id;
        }).collect(Collectors.toList());

        result.addAll(collect);

        return result;
    }

    @Override
    public List<String> listChildrenIdsAndOneself(String pid) {

        //只能获取当前机构下的文档
        List<String> list = listChildrenIds(pid);
        list.add(pid);
        CollUtil.removeBlank(list);
        return list;
    }

    @Override
    public List<Organization> listChildrens(String pid) {

        List<Organization> result = new ArrayList<>();

        List<Organization> allByPid = organizationRepository.findAllByPid(pid);
        List<Organization> collect = allByPid.stream().map(organization -> {
            String id = organization.getId();
            List<Organization> organizations = listChildrens(id);
            result.addAll(organizations);
            return organization;
        }).collect(Collectors.toList());

        result.addAll(collect);
        CollUtil.removeNull(result);
        return result;
    }

    @Override
    public List<String> listIdChildrens(String pid) {
        List<String> result = new ArrayList<>();
        List<Organization> allByPid = organizationRepository.findAllByPid(pid);
        allByPid.stream().map(organization -> {
            String id = organization.getId();

            result.add(id);
            return result;
        }).collect(Collectors.toList());
        result.add(pid);
        return result;
    }

    @Override
    public Organization find(String name) {

        Organization organization = organizationRepository.findByOrgNameOrOrgNum(name, name);

        return organization;
    }

    @Override
    public List<String> findAllPame(String name) {

        String resultStr = stringRedisTemplate.opsForValue().get(REDIS_ORG_PREFIX + name);
        if (StringUtils.isNotBlank(resultStr)) {
            JSONArray jsonArray = JSONUtil.parseArray(resultStr);
            List<String> list = JSONUtil.toList(jsonArray, String.class);
            return list;
        }

        List<String> result = new ArrayList<>();

        //用目前所有的单位去匹配，匹配到了就算有
        List<Organization> list = list();
        list.stream().forEach(organization -> {
            String orgName = organization.getOrgName();
            if (name.contains(orgName)) {
                String id = organization.getId();
                List<String> allPname = findAllPname(id);
                result.addAll(allPname);
                result.add(orgName);
            }
        });

        List<String> distinct = CollUtil.distinct(result);

        String str = JSONUtil.toJsonStr(distinct);
        stringRedisTemplate.opsForValue().set(REDIS_ORG_PREFIX + name, str, 1, TimeUnit.MINUTES);

        return distinct;
    }

    @Override
    public List<String> findAllPname(String id) {

        List<String> result = new ArrayList<>();
        Organization organization = get(id);
        String pid = organization.getPid();
        String pname = organization.getPname();
        result.add(pname);
        if (StringUtils.isNotBlank(pid)) {
            List<String> allPname = findAllPname(pid);
            result.addAll(allPname);
        }

        CollUtil.removeNull(result);

        return result;
    }

    @Override
    public String findPname(String id) {
        Organization organization = get(id);
        String pname = organization.getPname();
        return pname;
    }

    @Override
    public Organization get(String id) {

        Optional<Organization> repository = organizationRepository.findById(id);
        if (!repository.isPresent()) {
            throw new BaseException(DATA_NOT_EXIST.getCode(), "未找到该单位信息！");
        }

        Organization organization = repository.get();

        return organization;
    }

    @Override
    public List<String> getOrgRegionGroup() {

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder qb = QueryBuilders.boolQuery();

        //只能获取当前机构下的文档
        List<String> list = listChildrenIdsAndOneself(UserThreadLocal.getOrgId());
        if (CollUtil.isEmpty(list)) {
            return null;
        }
        qb.filter(new TermsQueryBuilder("id",list));

        TermsAggregationBuilder builder = AggregationBuilders.terms("region").field("region");
        queryBuilder.withQuery(qb);
        queryBuilder.addAggregation(builder);
        NativeSearchQuery build = queryBuilder.build();
        build.setMaxResults(0);

        SearchHits<Organization> search = elasticsearchRestTemplate.search(build, Organization.class);
        Aggregations aggregations = search.getAggregations();
        if (aggregations == null) {
            return null;
        }

        ParsedStringTerms orgName = aggregations.get("region");

        List<ParsedStringTerms.ParsedBucket> buckets = (List<ParsedStringTerms.ParsedBucket>) orgName.getBuckets();

        List<String> collect = buckets.stream().map(bucket -> {
            String keyAsString = bucket.getKeyAsString();

            return keyAsString;
        }).collect(Collectors.toList());

        return collect;
    }

    @Override
    public Boolean del(String id) {

        Organization organization = get(id);
        String orgName = organization.getOrgName();

        logService.save("单位", orgName, OPERATION_DEL);
        organizationRepository.deleteById(id);
        return true;
    }


    /**
     * 获取子机构
     * @param pid
     * @return
     */
    public List<OrganizationVO> getChildren(String pid) {

        List<Organization> organizations = organizationRepository.findAllByPidOrderByCreateTimeAsc(pid);

        List<OrganizationVO> collect = organizations.stream().map(organization -> {

            String id = organization.getId();
            String organizationName = organization.getOrgName();
            String organizationNumber = organization.getOrgNum();
            OrganizationVO organizationVO = new OrganizationVO();
            organizationVO.setId(id);
            organizationVO.setPname(organization.getPname());
            organizationVO.setPid(organization.getPid());
            organizationVO.setOrgName(organizationName);
            organizationVO.setOrgNum(organizationNumber);

            organizationVO.setChildren(getChildren(id));

            return organizationVO;
        }).collect(Collectors.toList());

        return collect;

    }

}
